using System;

namespace ScienceNET.Optimization
{

    public class OptimizerRP : OptimizerBase
    {
        private double[] parametersDelta;
        private double[] oldGradient;
        public double delta0 = 0.07;
        public double deltamax = 50;
        public double deltamin = 1E-7;
        public double delta_inc = 1.2;
        public double delta_dec = 0.5;


        public OptimizerRP(IOpimizable optimizedObject)
        {
            if ((optimizedObject.AllowedComputations & ComputeEnum.Gradient) == 0 || (optimizedObject.AllowedComputations & ComputeEnum.FunctionValue) == 0)
                throw new Exception("The resilient backpropagation optimizer needs a funcion value and a gradient from a model");
            this.parametersNumber = optimizedObject.ParametersNumber;
            parametersDelta = new double[this.parametersNumber];
            for (int parameter = 0; parameter < this.parametersNumber; parameter++) parametersDelta[parameter] = this.delta0;
            this.oldGradient = new double[this.parametersNumber];
            this.optimizedObject = optimizedObject;
            this.parameters = new double[this.parametersNumber];
            this.gradient = new double[this.parametersNumber];
        }





        public override void Step()
        {
            this.optimizedObject.Compute(ComputeEnum.FunctionValue | ComputeEnum.Gradient, this);
            for (int parameter = 0; parameter < this.parametersNumber; parameter++)
            {
                if (this.gradient[parameter] * oldGradient[parameter] > 0)
                {
                    parametersDelta[parameter] *= this.delta_inc;
                    if (parametersDelta[parameter] > deltamax) parametersDelta[parameter] = deltamax;
                }

                if (this.gradient[parameter] * oldGradient[parameter] < 0)
                {
                    parametersDelta[parameter] *= this.delta_dec;
                    if (parametersDelta[parameter] < deltamin) parametersDelta[parameter] = deltamin;

                }

                if (this.gradient[parameter] > 0) this.Parameters[parameter] -= parametersDelta[parameter];
                if (this.gradient[parameter] < 0) this.Parameters[parameter] += parametersDelta[parameter];
                this.oldGradient[parameter] = this.gradient[parameter];

            }

        }



    }
}